/*
 * @(#) $Header$
 *
 * Copyright (C) 2007  Forklabs Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.media.jai.operator;

import java.awt.RenderingHints;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.RenderableImage;
import java.util.Collection;
import javax.media.jai.CollectionImage;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptor;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.ParameterListDescriptor;
import javax.media.jai.RenderableOp;
import javax.media.jai.RenderedOp;
import javax.media.jai.registry.CollectionRegistryMode;
import javax.media.jai.registry.RenderableRegistryMode;
import javax.media.jai.registry.RenderedRegistryMode;
import ca.forklabs.media.jai.CollectionDescriptor;
import ca.forklabs.media.jai.FormatDataType;
import ca.forklabs.media.jai.SpectralFilter2D;
import ca.forklabs.media.jai.SpectralFilter3D;

/**
 * Class {@code SpectralFilterDescriptor} is an {@link OperationDescriptor}
 * describing the <em>spectralfilter</em> operation. This composite
 * operation consists of these four steps :
 * <blockquote>
 * format -&gt; fft -&gt; filter -&gt; ifft
 * </blockquote>
 * The <em>spectralfilter</em> operation takes two parameters, the filter
 * and the data type for the format operation :
 * <dl>
 *   <dt>filter</dt>
 *   <dd>This argument will provide the filter image to multiply to the Fourier
 *       transform.</dd>
 *   <dt>data type</dt>
 *   <dd>This argument is the type for the format operation. Data format can be
 *       found in enum {@link FormatDataType}. The default value is
 *       {@link FormatDataType#FLOAT}.</dd>
 * </dl>
 * <p>
 * Here is a description of each step :
 * <dl>
 *    <dt>format</dt>
 *    <dd>The format step change the type of the image pixel. Its behaviour can
 *        be controlled with the data type parameter.</dd>
 *    <dt>fft</dt>
 *    <dd>This step calculates the Fourier Transform, using no scaling.</dd>
 *    <dt>filter</dt>
 *    <dd>The filter image will be generated and multiplied to the Fourier
 *        transform.</dd>
 *    <dt>ifft</dt>
 *    <dd>This step calculates the Inverse Fourier Transform, using the
 *        dimension scaling.</dd>
 * </dl>
 * <p>
 *
 * <table border=1>
 * <caption>Resource List</caption>
 * <tr><th>Name</th>        <th>Value</th></tr>
 * <tr><td>GlobalName</td>  <td>SpectralFilter</td></tr>
 * <tr><td>LocalName</td>   <td>SpectralFilter</td></tr>
 * <tr><td>Vendor</td>      <td>ca.umontreal.iro.image.arcticice</td></tr>
 * <tr><td>Description</td> <td>Spectral Filter</td></tr>
 * <tr><td>DocURL</td>      <td>n/a</td></tr>
 * <tr><td>Version</td>     <td>$Version$</td></tr>
 * <tr><td>Arg0Desct</td>   <td>The spectral filter</td></tr>
 * <tr><td>Arg1Desct</td>   <td>The data type for the format operation</td></tr>
 * </table>
 *
 * <table border=1>
 * <caption>Parameter List for the <em>rendered</em> and <em>renderable</em> modes</caption>
 * <tr><th>Name</th>             <th>Class Type</th>         <th>Default Value</th></tr>
 * <tr><td>filter</td>           <td>SpectralFilter2D</td>   <td>NO_PARAMETER_DEFAULT</td>
 * <tr><td>type</td>             <td>FormatDataType</td>     <td>FormatDataType.FLOAT</td>
 * </table>
 * <table border=1>
 * <caption>Parameter List for the <em>collection</em> mode</caption>
 * <tr><th>Name</th>             <th>Class Type</th>         <th>Default Value</th></tr>
 * <tr><td>filter</td>           <td>SpectralFilter3D</td>   <td>NO_PARAMETER_DEFAULT</td>
 * <tr><td>type</td>             <td>FormatDataType</td>     <td>FormatDataType.FLOAT</td>
 * </table>
 *
 * @author   <a href="mailto:daniel.leonard at umontreal.ca?subject=ca.umontreal.iro.image.arcticice.media.jai.operator.SpectralDescriptor">Daniel Léonard</a>
 * @version $Revision$
 */
public class SpectralFilterDescriptor extends CollectionDescriptor {

//---------------------------
// Class variables
//---------------------------

   /** <em>serialVersionUID</em>. */
   private static final long serialVersionUID = -3138118927177422248L;

   /** The name of this operator. */
   public static final String NAME = "SpectralFilter"; //$NON-NLS-1$

   /** The name of the filter parameter. */
   public static final String FILTER_PARAMETER_NAME = "filter"; //$NON-NLS-1$
   /** The name of the initial format type parameter. */
   public static final String TYPE_PARAMETER_NAME = "type"; //$NON-NLS-1$

   /** The index in the parameter block of the filter parameter. */
   public static final int FILTER_PARAMETER_INDEX = 0;
   /** The index in the parameter block of the data type parameter. */
   public static final int TYPE_PARAMETER_INDEX = 1;

   /**
    * The resource strings that provide the general documentation and specify
    * the parameter list for this operation.
    */
   @SuppressWarnings("nls")
   private static final String[][] RESOURCES =
   {
      { "GlobalName",  SpectralFilterDescriptor.NAME, }, //$NON-NLS-1$
      { "LocalName",   SpectralFilterDescriptor.NAME, }, //$NON-NLS-1$
      { "Vendor",      "ca.umontreal.iro.image.arcticice", }, //$NON-NLS-1$ //$NON-NLS-2$
      { "Description", SpectralFilterDescriptor.getDescription(), }, //$NON-NLS-1$
      { "DocURL",      "n/a", }, //$NON-NLS-1$ //$NON-NLS-2$
      { "Version",     "$Version$", }, //$NON-NLS-1$ //$NON-NLS-2$
      { "arg0Desc",    SpectralFilterDescriptor.getArg0Description(), },
      { "arg1Desc",    SpectralFilterDescriptor.getArg1Description(), },
   };

   /** The supported modes. */
   private static final String[] SUPPORTED_MODES =
   {
      RenderedRegistryMode.MODE_NAME,
      RenderableRegistryMode.MODE_NAME,
      CollectionRegistryMode.MODE_NAME,
   };

   /** The name of the source, use default. */
   private static final String[] SOURCE_NAMES = null;

   /** The type of source for each mode. */
   private static final Class<?>[][] SOURCE_CLASSES = new Class<?>[][]
   {
      { Collection.class, },
      { Collection.class, },
      { Collection.class, },
   };

   protected static class SpectralHomomorphicParameterListDescriptor extends CollectionDescriptor.EmptyParameterListDescriptor {

      /**
       * Gets the number of parameters for this operator.
       * @return   always <em>2</em>.
       */
      @Override
      public int getNumParameters() {
         int num_parameters = 2;
         return num_parameters;
         }

      /**
       * {@inheritDoc}
       */
      @Override
      public Object getParamDefaultValue(String name) {
         int index;
         if (SpectralFilterDescriptor.FILTER_PARAMETER_NAME.equals(name)) {
            index = SpectralFilterDescriptor.FILTER_PARAMETER_INDEX;
            }
         else if (SpectralFilterDescriptor.TYPE_PARAMETER_NAME.equals(name)) {
            index = SpectralFilterDescriptor.TYPE_PARAMETER_INDEX;
            }
         else {
         // this call throws an exception
            return super.getParamDefaultValue(name);
            }

         Object[] values = this.getParamDefaults();
         Object value = values[index];
         return value;
         }

      /**
       * Gets the default values.
       * @return   no default value for the filter, type float for the data
       *           type.
       */
      @Override
      public Object[] getParamDefaults() {
         Object[] defaults = new Object[] {
// TODO : make a default filter with all 1s for real values and all 0s for imaginary values - watch for type SpectralFilter2D and SpectralFilter3D
            OperationDescriptor.NO_PARAMETER_DEFAULT,
            FormatDataType.FLOAT,
            };
         return defaults;
         }

      /**
       * {@inheritDoc}
       */
      @Override
      public String[] getParamNames() {
         String[] names = {
            SpectralFilterDescriptor.FILTER_PARAMETER_NAME,
            SpectralFilterDescriptor.TYPE_PARAMETER_NAME,
            };
         return names;
         }

      /**
       * Determines if the value is valid.
       * @return    {@code true} if the value is not {@code null},
       *            {@code false} otherwise.
       */
      @Override
      public boolean isParameterValueValid(String name, Object value) {
      // each parameter is an array, check that it is not null
      // and nothing more
         boolean is_valid = (null != value);
         return is_valid;
         }

      }


   /** The parameter list descriptor for the rendered and renderable modes. */
   private static final ParameterListDescriptor RENDERED_AND_RENDERABLE_PARAMETER_LIST_DESCRIPTOR = new SpectralHomomorphicParameterListDescriptor() {

      @Override
      public Class<?>[] getParamClasses() {
         Class<?>[] clazzes = new Class<?>[] {
            SpectralFilter2D.class,
            FormatDataType.class,
            };
         return clazzes;
         }

      };

   /** The parameter list descriptor for the collection mode. */
   private static final ParameterListDescriptor COLLECTION_PARAMETER_LIST_DESCRIPTOR = new SpectralHomomorphicParameterListDescriptor() {

      @Override
      public Class<?>[] getParamClasses() {
         Class<?>[] clazzes = new Class<?>[] {
            SpectralFilter3D.class,
            FormatDataType.class,
            };
         return clazzes;
         }

      };

   /** Description of the parameters. */
   private static final ParameterListDescriptor[] PARAMETER_LIST_DESCRIPTORS = new ParameterListDescriptor[]
   {
      SpectralFilterDescriptor.RENDERED_AND_RENDERABLE_PARAMETER_LIST_DESCRIPTOR,
      SpectralFilterDescriptor.RENDERED_AND_RENDERABLE_PARAMETER_LIST_DESCRIPTOR,
      SpectralFilterDescriptor.COLLECTION_PARAMETER_LIST_DESCRIPTOR,
   };


//---------------------------
// Constructor
//---------------------------

   /**
    * Constructor.
    */
   public SpectralFilterDescriptor() {
      super(RESOURCES, SUPPORTED_MODES, SOURCE_NAMES, SOURCE_CLASSES, PARAMETER_LIST_DESCRIPTORS);
      }


//---------------------------
// Class methods
//---------------------------

   /**
    * Creates and fills a new parameter block.
    * @param   mode   the rendering mode (usually rendered or renderable).
    * @param   source   the source image.
    * @param   filter  the filter
    * @param   type   the data type for the format operation.
    * @return   a new parameter block.
    */
   public static ParameterBlockJAI createParameterBlock(String mode, Object source, SpectralFilter2D filter, FormatDataType type) {
      String name = SpectralFilterDescriptor.NAME;
      ParameterBlockJAI pb = new ParameterBlockJAI(name, mode);
      pb.addSource(source);
      pb.setParameter(SpectralFilterDescriptor.FILTER_PARAMETER_NAME, filter);
      pb.setParameter(SpectralFilterDescriptor.TYPE_PARAMETER_NAME, type);
      return pb;
      }

   /**
    * Creates and fills a new parameter block.
    * @param   mode   the rendering mode (usually collection).
    * @param   source   the source image.
    * @param   filter  the filter
    * @param   type   the data type for the format operation.
    * @return   a new parameter block.
    */
   public static ParameterBlockJAI createParameterBlock(String mode, Object source, SpectralFilter3D filter, FormatDataType type) {
      String name = SpectralFilterDescriptor.NAME;
      ParameterBlockJAI pb = new ParameterBlockJAI(name, mode);
      pb.addSource(source);
      pb.setParameter(SpectralFilterDescriptor.FILTER_PARAMETER_NAME, filter);
      pb.setParameter(SpectralFilterDescriptor.TYPE_PARAMETER_NAME, type);
      return pb;
      }

   /**
    * Creates and fills a new parameter block for the rendered mode.
    * @param   source   the source image.
    * @param   filter  the filter.
    * @param   type   the data type for the format operation.
    * @return   a new parameter block.
    */
   public static ParameterBlockJAI createParameterBlock(RenderedImage source, SpectralFilter2D filter, FormatDataType type) {
      String mode = RenderedRegistryMode.MODE_NAME;
      ParameterBlockJAI pb = SpectralFilterDescriptor.createParameterBlock(mode, source, filter, type);
      return pb;
      }

   /**
    * Creates and fills a new parameter block for the renderable mode.
    * @param   source   the source image.
    * @param   filter  the filter.
    * @param   type   the data type for the format operation.
    * @return   a new parameter block.
    */
   public static ParameterBlockJAI createParameterBlock(RenderableImage source, SpectralFilter2D filter, FormatDataType type) {
      String mode = RenderedRegistryMode.MODE_NAME;
      ParameterBlockJAI pb = SpectralFilterDescriptor.createParameterBlock(mode, source, filter, type);
      return pb;
      }

   /**
    * Creates and fills a new parameter block for the collection mode.
    * @param   source   the source image.
    * @param   filter  the filter.
    * @param   type   the data type for the format operation.
    * @return   a new parameter block.
    */
   public static ParameterBlockJAI createParameterBlock(CollectionImage source, SpectralFilter3D filter, FormatDataType type) {
      String mode = CollectionRegistryMode.MODE_NAME;
      ParameterBlockJAI pb = SpectralFilterDescriptor.createParameterBlock(mode, source, filter, type);
      return pb;
      }

   /**
    * Performs the spatial homomorphic filter operation on a rendered image.
    * @param   source   the image to enhance.
    * @param   filter  the filter.
    * @param   type   the data type for the initial format.
    * @param   hints    the rendering hints, may be {@code null}.
    * @return   the rendered result image.
    */
   public static RenderedOp create(RenderedImage source, SpectralFilter2D filter, FormatDataType type, RenderingHints hints) {
      String operation = SpectralFilterDescriptor.NAME;
      ParameterBlockJAI pb = SpectralFilterDescriptor.createParameterBlock(source, filter, type);
      RenderedOp image = JAI.create(operation, pb, hints);
      return image;
      }

   /**
    * Performs the spatial homomorphic filter operation on a renderable image.
    * @param   source   the image to enhance.
    * @param   filter  the filter.
    * @param   type   the data type for the initial format.
    * @param   hints    the rendering hints, may be {@code null}.
    * @return   the rendered result image.
    */
   public static RenderableOp createRenderable(RenderableImage source, SpectralFilter2D filter, FormatDataType type, RenderingHints hints) {
      String operation = SpectralFilterDescriptor.NAME;
      ParameterBlockJAI pb = SpectralFilterDescriptor.createParameterBlock(source, filter, type);
      RenderableOp image = JAI.createRenderable(operation, pb, hints);
      return image;
      }

   /**
    * Performs the spatial homomorphic filter operation on a rendered image.
    * @param   source   the image to enhance.
    * @param   filter  the filter.
    * @param   type   the data type for the initial format.
    * @param   hints    the rendering hints, may be {@code null}.
    * @return   the rendered result image.
    */
   public static CollectionImage createCollection(CollectionImage source, SpectralFilter3D filter, FormatDataType type, RenderingHints hints) {
      String operation = SpectralFilterDescriptor.NAME;
      ParameterBlockJAI pb = SpectralFilterDescriptor.createParameterBlock(source, filter, type);
      CollectionImage image = (CollectionImage) JAI.createCollection(operation, pb, hints);
      return image;
      }


//---------------------------
// External resources methods
//---------------------------

   /**
    * Gets the description of this operation.
    * @return   the description message.
    */
   protected static String getDescription() {
      String key = Resources.SPECTRAL_FILTER_DESCRIPTION;
      String message = Resources.getLocalizedString(key);
      return message;
      }

   /**
    * Gets the description for the first argument, spectral filter.
    * @return   the description message.
    */
   protected static String getArg0Description() {
      String key = Resources.SPECTRAL_FILTER_ARG0_DESCRIPTION;
      String message = Resources.getLocalizedString(key);
      return message;
      }

   /**
    * Gets the description for the second argument, the format data type.
    * @return   the description message.
    */
   protected static String getArg1Description() {
      String key = Resources.SPECTRAL_FILTER_ARG1_DESCRIPTION;
      String message = Resources.getLocalizedString(key);
      return message;
      }

   }
